﻿using System;
using System.Collections.Generic;
using FamilyBook.Core.Entities;
using FamilyBook.Core.Exceptions;
using FamilyBook.Core.Repositories;

namespace FamilyBook.Core.Services
{
    public sealed class AccountService<TEntity> where TEntity : Account
    {
        private readonly SequenceService _sequenceService;
        //private readonly IGeneralRepository<Currency, string> _currencyRepository;
        private readonly GeneralService<TEntity, int> _acctGenService;
        private readonly GeneralService<Transaction, int> _trnGenService;
        private readonly IGeneralRepository<Product, int> _productRepository;

        public AccountService(GeneralService<TEntity, int> acctGenService,
            GeneralService<Transaction, int> trnGenService,
            IGeneralRepository<Product, int> productRepository,
            //IGeneralRepository<Currency, string> currencyRepository,
            SequenceService sequenceService)
        {
            _acctGenService = acctGenService;
            _trnGenService = trnGenService;
            _productRepository = productRepository;
            //_currencyRepository = currencyRepository;
            _sequenceService = sequenceService;
        }

        public TEntity Create(TEntity account)
        {
            account.Number = GenerateAccountNumber<TEntity>();
            account = _acctGenService.Create(account);
            return account;// _acctGenService.GetDetails(account.AccountId);
        }

        public TEntity GetDetails(int accountId)
        {
            return _acctGenService.GetDetails(accountId);
        }

        public void Pay(int srcAccountId, int dstAccountId, decimal amount, int? productId, string comment)
        {
            Account srcAccount = _acctGenService.GetDetails(srcAccountId);
            Account dstAccount = _acctGenService.GetDetails(dstAccountId);

            if (srcAccount.Currency.Code != dstAccount.Currency.Code)
                throw new AccountServiceErrorException("Pay operation failed. Currencies of source and destination accounts should be equal");

            if (srcAccount.Status != EntityStatus.Active)
                throw new AccountServiceErrorException(string.Format("Pay operation failed. Account '{0}' should be active", srcAccount.Number));
            if (dstAccount.Status != EntityStatus.Active)
                throw new AccountServiceErrorException(string.Format("Pay operation failed. Account '{0}' should be active", dstAccount.Number));

            Product product = productId.HasValue ? _productRepository.Read(productId.Value) : null;

            var trn = new Transaction()
                {
                    TransactionDate = DateTime.Now,
                    SrcAccount = srcAccount,
                    DstAccount = dstAccount,
                    Amount = amount,
                    Product = product,
                    Comment = comment
                };

            _trnGenService.Create(trn);
        }

        public IEnumerable<TEntity> GetAccounts()
        {
            return _acctGenService.GetItemsList();
        }

        public void Update(TEntity account)
        {
            _acctGenService.Update(account);
        }

        private string GenerateAccountNumber<TAccount>() where TAccount : Account
        {
            if (typeof(TAccount) == typeof(Account))
            {
                int counter = _sequenceService.GetNextValue(SequenceType.AccountNumber);
                return string.Format("A{0:D9}", counter);
            }

            throw new AccountServiceErrorException(string.Format("GenerateAccountNumber failed. The type '{0}' is not supported", typeof(TAccount).Name));
        }
    }
}
